1

Hi everybody out there!

My question is as follows:

I have a template class which stores pointer to a method of other classes (of course the template class is unaware initially of the types of classes to be passed to it). I made the constructor of the template class to take a pointer to the passed in class, and another argument which is the address of the method of the passed in class that I want to store, as follows:

    template <typename MyClass>
    TemplateClass
    {
    typedef void (MyClass::*MethodPointer)();
    MyClass* theClass;
    MethodPointer methodPointer;
public:
    TemplateClass(MyClass* cl, MethodPointer func) : theClass(cl), methodPointer(func){}

    void Excute()
    {
        return (theClass->*methodPointer)();
    }
    };

Then, I made a base class, and derived a subclass from it.

    class BaseClass
    {
        // other details are omitted...
        virtual void DoSomething();
    };

    class DerivedClass : public BaseClass
    {
        // other details are omitted...
        void DoSomething();
    };

    // the definitions of the methods is omitted because it had no relevance to the issue

Then, I made a typedef of the template class taking the base class as the parameter such as:

    typedef TemplateClass<BaseClass> TypedTemplateClass;

Then, when I try to pass a pointer of the derived class to the constructor of TypedTemplateClass, the compiler says that it cannot convert the argument from the derived class to the base class, as follows:

    DerivedClass* myDerivedObject = new DerivedClass();
    TypedTemplateClass* myTemplateObject = new TypedTemplateClass(myDerivedObject, &DerivedClass::DoSomething);

But if I pass an object of the base class itself, everything works well! Just as follows:

    BaseClass* baseObject = new BaseClass();
    TypedTemplateClass* myTemplateObject2 = new TypedTemplateClass(baseObject, &BaseClass::DoSomething);

So, Can somebody enlighten me to get over this problem? I am aware that the problem lies in that the typed template class is expecting a base class object, but I need to pass the derived class object - because I intend to make different derived classes and be able to pass their methods polymorphically to the template class. I am also aware that I could just ignore the TypedTemplateClass definition and just create template class objects each with a different derived class type. However, the above proposal is my intention.

I am using visual studio IDE

Thanks in advance for your interest and kind help.

5
  • Is it normal that in your DerivedClass that DoSomething is not virtual ? Commented Aug 31, 2012 at 13:31
  • 1
    You're first two snippets are not valid C++. It's hard to tell what you're really doing... Commented Aug 31, 2012 at 13:33
  • @J.N. not necessarily, it can be virtual. Commented Aug 31, 2012 at 13:47
  • 1
    Yes and I find it to be extremely confusing. Anyone I find doing it gets a backhand. Be explicit! Commented Aug 31, 2012 at 13:49
  • What you are trying to achieve with TemplateClass is what std::function was made for. I highly recommend to use it... Commented Sep 1, 2012 at 11:46

3 Answers 3

4

The issue is that you can't convert a pointer-to-method from pointer-to-method-of-derived-type to pointer-to-method-of-base-type, because pointer-to-methods are contravariant with the object type.

Consider:

Base instance;
void (Base::*pmethod)();
pmethod = &Base::doSomething;
(instance.*pmethod)();    // OK

If you were allowed to write

pmethod = &Derived::doSomethingElse;

then you could use pmethod to invoke Derived::doSomethingElse on an instance of type Base.

Under Liskov's substitution principle, a Derived object (reference) is-a Base object (reference), because anything you can do to a Base you can do to a Derived, but a pointer-to-method-of-Derived is-not-a pointer-to-method-of-Base; indeed, it's the other way round (a pointer-to-method-of-Base is-a pointer-to-method-of-Derived), which is why we say that pointer-to-methods are contravariant:

void (Derived::*pmethod)() = &Base::doSomething;

Probably the best bet in your case would be to write a templated constructor and use type erasure to hide the Derived method pointer's type; in the following

template<typename T>
TemplateClass(T *instance, void (T::*pmethod)());

the two Ts in the type of the constructor can cancel each other out to give the function signature void (). You could do this with a std::function<void ()> member:

Base *instance;
std::function<void ()> type_erased_method;

template<typename T>
TemplateClass(T *instance, void (T::*pmethod)()):
    instance(instance),
    type_erased_method(std::bind(pmethod, instance))
{
}
Sign up to request clarification or add additional context in comments.

6 Comments

thanks for the explanation. I tried your way and now it compiles successfully. But, on runtime when the application arrives to the stage of excuting the method whose address is stored inside the template class instance, it gets access violation error and stops there! Any idea about this.
the error is: Unhandled exception at 0xcdcdcdcd in ####.exe: 0xC0000005: Access violation. (where #### is the file name)
@user1638717 are you sure the instance has not been deleted? Try it with a short example (e.g. without using your TemplateClass) and see if that works.
@user1638717 0xcdcdcdcd looks like the memory pattern that VS assigns to uninitialized memory. See softwareverify.com/memory-bit-patterns.php
I will try to do something about it. Thanks a lot for your invaluable suggestions.
|
0

What is this supposed to mean: TemplateClass(MyClass* class, &MyClass::SomeMethod); It's not a valid C++ code. Why don't you stick with c++ standard library? If you need to store a callable, use std::function constructed from a lambda. The compiler and the library will take care of all necessary conversions...

1 Comment

I'm sorry, that was a typing mistake. This is the way I call the constructor somewhere else, not the declaration or definition. I already corrected this mistake in my question. thanks.
0

Well, the project that I worked upon was a trial to make a mechanism to handle events in C++ similar – more or less to C# way of Events and EventHandlers. So, after research online and inside some texts and figuring out the suitable ways, I wrote two template classes: One is to store a pointer for the member method and called it ‘EventHandler’. The other one is to store a map of event handlers and to invoke them upon need; this is the ‘Event’ class. Then I wrote two ‘normal’ classes: one to be the event firing class and the other to be the responding or listening class. The initial versions of the Event and EventHandler classes were as follows:

#include <functional>

namespace eventhandling
{
#ifndef __BASIC_EVENT_HANDLER__
#define __BASIC_EVENT_HANDLER__

////////////////////////////////////////////////////////////////////////////
// the root event handler class
////////////////////////////////////////////////////////////////////////////
template <typename empty = int, empty = 0>
class BaseEventHandler
{
public:
    virtual void Excute() = 0;
};


///////////////////////////////////////////////////////////////////////////
// the basic derived event handler class; the class which will wrap the
// methods of other classes which want to respond to specific event(s)..
///////////////////////////////////////////////////////////////////////////
template <typename ResponderType>
class EventHandler : public BaseEventHandler < >
{
    typedef void (ResponderType::*MethodPointer());

    ResponderType* responder;
    MethodPointer methodPointer;

public:
    EventHandler(ResponderType* resp, MethodPointer func) : responder(resp), methodPointer(func)
    {}

    void Excute()
    {
        return methodPointer();
    }
};

#endif
}

#include "BasicEventHandler.h"
#include <map>

namespace eventhandling
{
#ifndef __BASIC_EVENT__
#define __BASIC_EVENT__

////////////////////////////////////////////////////////////////////////////////////////////////
// the event class which will receive these event handlers, stores them in a map object,
// and call them squentially when invoked from within the event firing method...
////////////////////////////////////////////////////////////////////////////////////////////////

// the template takes no parameters, so I added an empty parameter, just because
//it cannot ignore the parameter list, otherwise it will be considered template specialization
template <typename empty = int, empty = 0>
class BasicEvent
{
    //store the eventhandlers in a map so that I can track them from outside the class by id
    typedef std::map<int, BaseEventHandler<empty>* > Responders;
    Responders responders;
    int respondersCount;

public:
    BasicEvent() : respondersCount(0)
    {}

    // classical add method templatized so that it accepts any object
    template <typename Responder>
    int Add(Responder* sender, void (Responder::*memberFunc)())
    {
        responders[respondersCount] = (new EventHandler<Responder>(sender, memberFunc));
        respondersCount++;
        return respondersCount - 1;
    }

    // simple method to clean up the map memory after done with the eventhandlers
    void Remove(int responderID)
    {
        Responders::iterator it = responders.find(responderID);
        if (it == responders.end())
            return;
        delete it->second;
        responders.erase(it);
    }

    // method which invokes all the eventhandlers alltogether without control from the outside
    void Invoke()
    {
        Responders::iterator it = responders.begin();
        for (; it != responders.end(); ++it)
        {
            it->second->Excute();
        }
    }

    // method which invokes only the eventhandler whose id has been passed to it
    void Invoke(int id)
    {
        Responders::iterator it = responders.find(id);
        if (it != responders.end())
            it->second->Excute();
    }

    // overloaded operator+= to replace the functionality of method Add()
    template <typename Responder>
    void operator+=(EventHandler<Responder>* eventhandler)
    {
        responders[respondersCount] = eventhandler;
        respondersCount++;
    }

    // overloaded operator -= to replace the functionality of method Remove()
    void operator-=(int id)
    {
        Responders::iterator it = responders.find(id);
        if (it == responders.end())
            return;
        delete it->second;
        responders.erase(it);
    }

    //simple method which gives the size of the map object
    int Size()
    {
        return respondersCount;
    }
};
#endif
}

Then, I wanted to get rid of the explicit ‘<…..>’ template syntax upon creating a new EventHandler object, which is obviously what is the case in C#, so, I made it simply like the following:

    typedef EventHandler<MyClass> SomeEventFired_EventHandler;

so that when I need to create a new object of this template, I just need to write:

    MyClass* myObject = new MyClass();
    MyEventFiringClass* firingObject = new MyEventFiringClass();
    firingObject->OnFired += new SomeEventFired_EventHandler(myObject, &MyClass::SomeMethod);

Of course, the complete code example later will make it more clear! Here came my question, where I wanted to be able to pass objects of derived classes of MyClass. The problem was that the template of EventHandler as shown above did not accept such derived objects, because it was expecting objects of the base class exclusively and the compiler complained that it cannot convert from the derived class to the base class. And here comes the invaluable help of ecatmur, when he showed me the proper way of making the constructor of the EventHandler class templated. That way, when I type defined SomeEventFired_EventHandler using MyClass as a base class, I became able to pass any object – and its method – to its constructor, as long as the object is from a class derived from MyClass. This was my ultimate aim in achieving the polymorphic feature of the EventHandler. I wanted this feature, because if you check the EventHandlers in C#, you can see that System::EventHandler is polymorphic, that it accepts different objects from classes which are essentially derived from class Object as I guess. So, here is the full example, with the rectified EventHandler class based on ecatmur solution, for you guys to review, and I hope you will find it of some benefit. Eventually, you can derive from the BaseEventHandler class so that derived EventHandlers can store methods with different return types and different argument parameters, because the basic one that is shown here accepts methods which return void and take void (I believe you can do so by just changing the declaration of std::function<> to make it accept other types of methods, e.g.,

std::function<int(int)> 

,and so on).

The event class is the same as above…

#include <functional>

namespace eventhandling
{
#ifndef __BASIC_EVENT_HANDLER__
#define __BASIC_EVENT_HANDLER__

////////////////////////////////////////////////////////////////////////////
// the root event handler class
////////////////////////////////////////////////////////////////////////////
template <typename empty = int, empty = 0>
class BaseEventHandler
{
public:
    virtual void Excute() = 0;
};


///////////////////////////////////////////////////////////////////////////
// the basic derived event handler class; the class which will wrap the
// methods of other classes which want to respond to specific event(s)..
///////////////////////////////////////////////////////////////////////////
template <typename ResponderType>
class EventHandler : public BaseEventHandler < >
{
    std::function<void ()> type_erased_method;
    ResponderType* responder;

public:

    template<typename T>
    EventHandler(T* resp, void (T::*MethodPointer)()) : responder(resp), type_erased_method(std::bind(MethodPointer, resp))
    {}

    void Excute()
    {
        return type_erased_method();
    }
};

#endif
}

The event firing class header file…

#include <iostream>
#include <string>
#include "BasicEvent.h"

namespace eventhandling
{
#ifndef __FONT_SIMULATOR__
#define __FONT_SIMULATOR__

typedef BasicEvent<> FontEvent;
typedef std::string s;

class FontSimulator
{

private:
    s fontName;
    s fontSize;
    s fontStyle;
public:
    FontSimulator();
    FontSimulator(s name, s size, s style);
    ~FontSimulator();

    FontEvent OnDraw;

    void DrawText();

    // the setting methods
    void SetFontName(s n) {fontName = n;}
    void SetFontSize(s si) {fontSize = si;}
    void SetFontStyle(s st) {fontStyle = st;}

    // the getting methods
    s GetFontName() {return fontName;}
    s GetFontSize() {return fontSize;}
    s GetFontStyle() {return fontStyle;}
};
#endif
}

its source file, .cpp

#include "FontSimulator.h"

using namespace eventhandling;

FontSimulator::FontSimulator() : fontName("Default Name"), fontSize ("Default Size"), fontStyle("Default Style")
{
}

FontSimulator::FontSimulator(s fName, s fSize, s fStyle) : fontName(fName), fontSize(fSize), fontStyle(fStyle)
{
}

FontSimulator::~FontSimulator()
{
delete this;
}

void FontSimulator::DrawText()
{
std::cout << "Initialization of font done!" << std::endl << std::endl;
std::cout << fontName << std::endl;
std::cout << fontSize << std::endl;
std::cout << fontStyle << std::endl << std::endl;

for (int i = 0; i < OnDraw.Size(); ++i)
{
    OnDraw.Invoke(i);
    std::cout << "the #" << i + 1 << " responder method called!" << std::endl << std::endl;
    std::cout << fontName << std::endl;
    std::cout << fontSize << std::endl;
    std::cout << fontStyle << std::endl << std::endl;
}
for (int j = 0; j < OnDraw.Size(); j++)
{
    //OnDraw.Remove(j);
    OnDraw -= j;
}

        std::cout << "The finishing font work after all the event handler are called!" << std::endl <<std::endl;
   }

The abstract base class which handles the event of the font class…

#include "BasicEventHandler.h"

namespace eventhandling
{
#ifndef __IFONT_CLIENT__
#define __IFONT_CLIENT__

class IFontClient
{
public:
    IFontClient(){};
    ~IFontClient(){delete this;}
    virtual void SetupFont() = 0;
};

typedef EventHandler<IFontClient> FontEventHandler;

#endif
}

The derived class from IFontClient… header file first

#include "BasicEventHandler.h"
#include "BasicEvent.h"
#include "FontSimulator.h"
#include "IFontClient.h"

namespace eventhandling
{
#ifndef __CONTROL_SIMULATOR__
#define __CONTROL_SIMULATOR__

class ControlSimulator : public IFontClient
{
protected:
    std::string caption;
    FontSimulator* font;

public:
    ControlSimulator();
    ControlSimulator(std::string theCaption, FontSimulator* theFont);
    ~ControlSimulator();

    virtual void Draw();
    virtual void SetupFont();

    void SetCaption(std::string c) {caption = c;}
    std::string GetCaption() {return caption;}
};

#endif
}

its source file .cpp

#include "ControlSimulator.h"

namespace eventhandling
{
ControlSimulator::ControlSimulator() : caption("Default Caption"), font(new FontSimulator())
{
}

ControlSimulator::ControlSimulator(std::string c, FontSimulator* f) : caption(c), font(f)
{
}

ControlSimulator::~ControlSimulator()
{
    delete this;
}

void ControlSimulator::Draw()
{
    std::cout << "Drawing " << caption << " is done!" << std::endl << std::endl;
}

void ControlSimulator::SetupFont()
{
    std::string costumProperty = caption;
    font->SetFontName(costumProperty.append(", Costumized Font Name"));

    costumProperty = caption;
    font->SetFontSize(costumProperty.append(", Costumized Font Size"));

    costumProperty = caption;
    font->SetFontStyle(costumProperty.append(", Costumized Font Style"));
}
}

The main entry to test the app

#include "ControlSimulator.h"

using namespace eventhandling;

int main(int argc, char** argv)
{
char c;

FontSimulator* font = new FontSimulator();
ControlSimulator* control1 = new ControlSimulator("Control one", font);
ControlSimulator* control2 = new ControlSimulator("Control two", font);

control1->Draw();
control2->Draw();

font->OnDraw += new FontEventHandler(control1, &ControlSimulator::SetupFont);
font->OnDraw += new FontEventHandler(control2, &ControlSimulator::SetupFont);

font->DrawText();

std::cout << "Enter any character to exit!" << std::endl;
std::cin >> c;

return 0;
}

Comments

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.